﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using IGolovkine.Core.Diagnostics;

namespace IGolovkine.ShoutCastRipper
{
    public class ContentTitleProbingPolicy : IContentTitleProbingPolicy
    {
        private readonly ILogWriter _logWriter;
        private readonly TimeSpan _minProbingPeriod;
        private readonly TimeSpan _maxProbingPeriod;
        private readonly Timer _timer;
        
        public event EventHandler<EventArgs> ProbeContentTitle;

        private DateTime _nextReconnectAttempt;

        public ContentTitleProbingPolicy(
            TimeSpan minProbingPeriod, 
            TimeSpan maxProbingPeriod,
            ILogWriter logWriter)
        {
            Debug.NotDefault(() => minProbingPeriod);
            Debug.NotDefault(() => maxProbingPeriod);
            Debug.NotDefault(() => logWriter);
            _minProbingPeriod = minProbingPeriod;
            _maxProbingPeriod = maxProbingPeriod;
            

            _timer = new Timer(new TimerCallback(this.OnTimerCallBack));
            _nextReconnectAttempt = DateTime.MaxValue;
            _logWriter = logWriter;
        }

        private void OnTimerCallBack(object state)
        {
            this.OnProbeContentTitle(EventArgs.Empty);
        }

        protected virtual void OnProbeContentTitle(EventArgs args)
        {
            if (this.ProbeContentTitle != null)
            {
                this.ProbeContentTitle(this, args);
            }
        }

        public void Schedule(Exception reason)
        {
            TimeSpan sleepingTime;
            if (reason != null)
            {
                sleepingTime = _maxProbingPeriod;
                _logWriter.WriteLine("Scheduled to reconnect in {0} due to '{1}'", sleepingTime, reason.GetType().FullName);
            }
            else
            {
                sleepingTime = GetSleepingTime(DateTime.Now);
                _logWriter.WriteLine("Scheduled to reconnect in {0}", sleepingTime);
            }
            
            _nextReconnectAttempt = DateTime.Now + sleepingTime;
            _timer.Change(sleepingTime, TimeSpan.FromMilliseconds(-1));
        }
        
        private bool TimeWithingMargins(TimeSpan time, TimeSpan maxSampleTime)
        {
            return (time.Minutes) >= 10 && (time.Minutes + maxSampleTime.TotalMinutes) <= 50;
        }

        private TimeSpan GetSleepingTime(DateTime currentTime)
        {
            TimeSpan min = _minProbingPeriod;
            TimeSpan max = _maxProbingPeriod;

            TimeSpan time = currentTime - new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, 0, 0);

            if (this.TimeWithingMargins(time, max))
            {
                return max;
            }
            else
            {
                return min;
            }
        }

        public void Dispose()
        {
            _timer.Dispose();
        }
    }
}
